<?php
/**
 *
 *
 */
use Yz_Access_Interface as Access;

abstract class Yz_Controller_Abstract
    extends Yz_Class
    implements Yz_Controller_Interface
{

    /** Type of access
     *      NOTE: Constant because it can be useful to test it externally
     *  @const int
     */
    const ACCESS = Access::AC_PUBLIC;

    /** controls whether controller appears in `actions menu`
     *  @var bool
     */
    const IN_MENU = false;

    /** NOTE static property shared among all derived classes, do not override unless really necessary!
     *  @var bool
     */
    protected static $isAdmin = false;

    /** Controller's name
     *  @var string
     */
    protected $name;

    /** Reference to the module
     *  @var Yz_Module_Interface
     */
    protected $module;

    /** Reference to the view
     *  @var Yz_View_Interface
     */
    protected $view;

    /** the name of a view class which will be used
     *      NOTE: A protected variable (rather than const) because
     *      not designed to be externally available, but possible to modify internally
     *  @var string
     */
    protected $viewClass = 'Yz_View_Default';



    /****************************
     *      Public methods      *
     ****************************/


    /** Model's constructor requires module
     *  @param Yz_Module_Interface $module
     */
    public function __construct( Yz_Module_Interface $module )
    {
        debug();
        $this->module = $module;
        $this->name = $this->shortClassName();
    }

    /** handle GET mothod request
     *  @param Yz_Array $args
     *  @return chainable|string|false
     */
    public function doGet( Yz_Array $args )
    {
        throw new Yz_Access_Exception_Forbidden("Access via GET method denied");
    }

    /** handle POST mothod request
     *  @param Yz_Array $args
     *  @param Yz_Hash $post
     *  @return chainable|string|false
     */
    public function doPost( Yz_Array $args, Yz_Hash $post )
    {
        throw new Yz_Access_Exception_Forbidden("Access via POST method denied");
    }

    /** handle errors (exceptions)
     *  @param Yz_Exception $ex
     *  @return chainable|string|false
     */
    public function doError( Yz_Exception $ex )
    {
        return false;
    }

    /** get the associated view
     *  @return Yz_View_Interface
     */
    public function getView()
    {
        $view =& $this->view;
        if (null === $view) {
            $view = new $this->viewClass();
        }
        return $view;
    }

    /**
     *  @return Yz_HTTP_Request
     */
    protected function getRequest()
    {
        return Yz_HTTP_Request::getInstance();
    }

    /**
     *  @return Yz_HTTP_Response
     */
    protected function getResponse()
    {
        return Yz_HTTP_Response::getInstance();
    }


    /** check if client is eligible to call this controller
     *  @return chainable
     *  @throw Yz_Access_Exception if the current user's access privileges do not match controller's
     */
    protected function checkAccess()
    {
        debug();
        switch (static::ACCESS) {
            case Access::AC_PUBLIC:
                break;
            case Access::AC_ADMIN:
                if (! $this->isAdmin()) {
                    $ex = new \Yz_Exception_ControllerNotFound();
                    throw $ex->withArgs(array(
                        'controllerName' => $this->name,
                        'moduleName' => $this->module->getName(),
                        ));
                }
                break;
            case Access::AC_VALID_USER:
                // not authenticated
                $user = Yz_User_Default::getInstance();
                if ($user->isAuthenticated()) {
                    break;
                }
            case Access::AC_ROLE:
            default:
                throw new \Yz_Access_Exception();
        }
        return $this;
    }



    /**
     *  @return bool
     */
    protected function isAdmin()
    {
        return self::$isAdmin;
    }

    /**
     *  @return chainable
     */
    protected function enterAdmin()
    {
        debug();
        self::$isAdmin = true;
        return $this;
    }

    /**
     *  @return chainable
     */
    protected function exitAdmin()
    {
        debug();
        self::$isAdmin = false;
        return $this;
    }

}
